﻿using System;
using System.Diagnostics;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

namespace zijian666.Converts.Convertor.Base;

/// <summary>
/// 带日志功能得到转换器代理
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
internal class TraceConvertor<T> : IConvertor<T>
{
    private static readonly DiagnosticListener _trace = new("zijian666.Converts");

    public TraceConvertor(IConvertor<T> convertor!!) => InnerConvertor = convertor;

    /// <inheritdoc />
    public uint Priority => InnerConvertor.Priority;

    /// <summary>
    /// 内部转换器
    /// </summary>
    public IConvertor<T> InnerConvertor { get; }

    /// <summary>
    /// 为避免在记录时调用 <seealso cref="Converts.To{T}(object)"/> 引起的死循环
    /// </summary>
    [ThreadStatic]
    private static bool _tracing = false;

    /// <inheritdoc />
    public ConvertResult<T> Convert(IConvertContext context!!, object? input)
    {
        if (_tracing || !_trace.IsEnabled())
        {
            var r = InnerConvertor.Convert(context, input);
            if (r.Exception is not null && !r.Exception.Data.Contains("Convert"))
            {
                r.Exception.Data.Add("Convert", InnerConvertor);
                r.Exception.Data.Add("InputValue", input);
                r.Exception.Data.Add("OutputType", typeof(T));
            }
            return r;
        }

        var activity = new Activity(InnerConvertor.GetType().GetFriendlyName());
        try
        {
            _tracing = true;
            _ = _trace.StartActivity(activity, input);
            _tracing = false;
            var result = InnerConvertor.Convert(context, input);
            _tracing = true;
            var ex = result.Exception;
            if (ex is not null && !ex.Data.Contains("Convert"))
            {
                ex.Data.Add("Convert", InnerConvertor);
                ex.Data.Add("InputValue", input);
                ex.Data.Add("OutputType", typeof(T));
            }
            _trace.StopActivity(activity, ex as object ?? result.Value);
            return result;
        }
        catch (Exception e)
        {
            _trace.StopActivity(activity, e);
            throw;
        }
        finally
        {
            _tracing = false;
        }
    }

    /// <summary>
    /// 显示在调试器中的内容
    /// </summary>
    /// <returns></returns>
    private string GetDebuggerDisplay() => $"Trace({InnerConvertor.GetType().GetFriendlyName()},out={typeof(T).GetFriendlyName()})";
}
